#pragma once
#include <vector>
#include <queue>
#include <sstream>
#include <map>
#include <stdlib.h>
#include <math.h>
#include "PokemonLoader.h"
#include "PokemonDefinition.h"
#include "PokemonAttack.h"

class Pokemon
{
public:
	Pokemon() {};
	Pokemon(const PokemonData& _data, int _level);
	Pokemon(const Pokemon& _source);
	~Pokemon();

	friend std::ostream& operator<<(std::ostream& _out, const Pokemon& _source);
	Pokemon& operator=(const Pokemon& _source);
	bool operator==(const Pokemon& _source) const;

	void ForgetAttack(int _index);
	void InitPokemon();
	std::queue<std::string> Attack(PokemonAttack& _attack, Pokemon& _enemy);
	std::queue<std::string> EndTurnStatus();
	void InflictDamage(int _damage, std::queue<std::string>& _out);
	void Heal(int _amount);
	void InflictStatus(const AttackData& _data, std::queue<std::string>& _out);
	void InflictStatModifier(const AttackData& _data, std::queue<std::string>& _out);
	void SetCurrentStats();
	void SetWaiting(bool _wait) { m_Waiting = _wait; }
	void UpdateEffortValue(const Pokemon& _enemy);
	std::queue<std::string>  FightWon(const Pokemon& _enemy);
	void LevelUp();
	void Evolve();
	bool CanAttack(std::queue<std::string>& _out);

	//Setters & Getters
	int GetNextAttack() const { return m_NextAttack; }
	int GetNumber() const { return m_Number; }
	std::string GetName() const { return m_Name; }
	int GetMaxHP() const { return m_Health; }
	int GetCurrentHP() const { return m_CurrentHealth; }
	int GetAttack() const { return m_Attack; }
	int GetCurrentAttack() const { return m_CurrentAttack; }
	int GetDefense() const { return m_Defense; }
	int GetSpAttack() { return m_SpAttack; }
	int GetSpDefense() { return m_SpDefense; }
	int GetSpeed() const { return m_Speed; }
	bool GetEvolve() const { return m_CanEvolve; }
	int GetLevel() const { return m_Level; }
	int GetXP() const { return m_CurrentXP; }

	void SetCurrentXP(int _xp) { m_CurrentXP = _xp; }
	void SetHasAttacked(bool _flag) { m_Attacked = _flag; }
	std::vector<PokemonType> GetTypes() const { return m_Types; }
	std::vector<PokemonAttack> GetAttacks() const { return m_Attacks; }
	PokemonAttack& GetAttackAt(int _index) { return m_Attacks[_index]; }

	bool IsDead() const { return m_CurrentHealth == 0; }
	bool HasAttacked() const { return m_Attacked; }
	bool IsWaiting() const { return m_Waiting; }
	bool IsAffectedBy(const StatusEffect& _effect) const;
	bool HasType(const PokemonType& _type) const;

private:
	std::queue<std::string> m_Out;

	int m_Number;
	std::string m_Name;
	int m_Health;
	int m_Attack;
	int m_Defense;
	int m_SpAttack;
	int m_SpDefense;
	int m_Speed;
	float m_Accuracy;
	bool m_CanEvolve;
	int m_EvolveLevel;

	int m_CurrentHealth;
	int m_CurrentAttack;
	int m_CurrentDefense;
	int m_CurrentSpAttack;
	int m_CurrentSpDefense;
	int m_CurrentSpeed;
	int m_CurrentXP;
	int m_Level;

	int m_IVs[STAT_COUNT];
	int m_EVs[STAT_COUNT];
	bool m_Waiting;
	bool m_Attacked;
	int m_NextAttack;

	std::vector<PokemonType> m_Types;
	std::vector<PokemonAttack> m_Attacks;
	std::vector<StatusAilmentData> m_Statuses;
	
	void ClearOutput();
	void KillPokemon(std::queue<std::string>& _out);
	void RemoveAllStatus();
	void SetBaseStats(const PokemonData& _data);
	void SetPokemonAttacks();
	int StatCalculation(int _baseStat, int _statIndex);
	int HPCalculation(int _baseStat, int _statIndex);

	int DamageCalculation(PokemonAttack& _attack, const Pokemon& _enemy);
	float ModifierCalculation(PokemonAttack& _attack, const Pokemon& _enemy);
	int CriticalHitCalculation() const;
	float SameTypeAttackBonus(PokemonType _type);
	float TypeModifier(PokemonAttack& _attack, const Pokemon& _enemy);

	bool IsConfused() const;
	std::string GetAttackAtLevel(int _level) const;
	int GetAttackIndex(std::string _attackName) const;
	void UpdateXP(int _xpGain);
};